The control displays itself as a regular button, except that it has a glyph (a little arrow) on the right signalling that it displays something when you click it. All it does is to display a popup menu. The popup menu is custom drawn because I wanted to be able to add colourful icons to it. The button is also ownerdrawn because it needs to paint the dropdown-glyph on the right.
The problem with ownerdrawn buttons is that on Windows XP and greater we need to paint
it with the current theme. Before Windows XP it was simply a matter of using the BS_OWNERDRAWN
style. Unfortunately this also meant that we had to repaint the entire button exterior as well. Since a button
always behaved the same, we could use the DrawFrameControl()
API to do much of the drawing.
Along came Windows XP and changed all that. The DrawFrameControl()
no longer works and
we'll have to dip into the Theme API to get the job done. Well, not quite... because the old Windows Common Controls
ListView
and TreeView
introduced a new ownerdraw method:
the NM_CUSTOMDRAW
notifications. And in Windows XP the Button control inherits this behaviour too
(as well as supporting the old WM_DRAWITEM
-range of messages).
The trick to drawing an ownerdraw button under Windows XP is not to draw the button at all.
The Button sends WM_NOTIFY / NM_CUSTOMDRAW
notifications to the parent.
If we wanted to drastically change the look of the button, we would start to custom paint here but since
we just want to add an icon and some visual decorations, we'll answer the notification with
the CDRF_NOTIFYPOSTPAINT
code. This instructs Windows to paint the button
entirely. When done, Windows calls us again with NM_CUSTOMDRAW
and allows
us to paint on top of the button image.
By settings the button window text to an empty string, Windows won't paint text on the button and so we can justify the text correctly, paint it, and make room for painting the new dropdown-arrow.
Unfortunately setting the button caption to an empty string activates a bug in the Windows XP theming engine, so it doesn't generate the
CDDS_POSTPAINT
notification any longer. So set it to a single space character or similar.
So to properly draw an ownerdrawn button in both Windows 98 and Windows XP, we'll need to support both
WM_DRAWITEM
and the new NM_CUSTOMDRAW
message.
The only good thing about this is that the code for drawing under Windows XP is much simpler
than under Windows 98.
Well, actually drawing under Windows XP is not simpler. Had I done this corrcetly I would still have
needed the Theme API to draw the text in the correct font and color. The same goes for the menu
background and text. The WTL library does offer a wrapper class for the theme calls, however it quickly
turns into a lot of mucking about.
In this sample though, I just print the text using standard GDI calls.
How to use it
OK, back to the Menu Button control. It paints the button, but also custom paints the menu. The menu can have checkmarks (with theBMS_EX_CHECKBOXES
style
set in the new SetExtendedMenuStyle()
method). Another style allows you to
add icons to each menu item.
To use it place a button control on your dialog.
Then add a member variable to your dialog implementation file...
CButtonMenuCtrl m_ctrlMenu
In the OnInitDialog()
event handler, add the following lines:
LRESULT OnInitDialog(UINT /*uMsg*/,
WPARAM /*wParam*/,
LPARAM /*lParam*/,
BOOL& /*bHandled*/)
{
...
m_ctrlMenu.SubclassWindow(GetDlgItem(IDC_BUTTON1));
m_ctrlMenu.SetMenu(IDM_POPUP);
...
}
To handle checkmarks in the menu, use the regular WTL UPDATE_UI_MAP
stuff or handle WM_INITMENUPOPUP
in the dialog.
Finally add the following reflection macro to your dialog's message map:
BEGIN_MSG_MAP(CMainDlg)
...
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
Source Code Dependencies
Microsoft Visual C++ 6.0Microsoft WTL 7.5 Library
Download Files
Source Code (5 Kb) |